﻿// $Id: SvnClientUIHandler.cs 1003 2009-01-11 23:47:44Z rhuijben $
//
// Copyright 2007-2009 The SharpSvn Project
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using SharpSvn.Security;
using SharpSvn.UI;
using SharpSvn.UI.Authentication;
using System.Drawing;
using System.Runtime.InteropServices;
//using SharpSvn.UI.Properties;
using System.ComponentModel;
using System.Windows.Forms.Design;

namespace SubVisual.GUI
{
    class VssCredentialsUI
    {
        readonly IWin32Window _window;
        ISynchronizeInvoke _synchronizer;

        sealed class ZeroWindowHandle : IWin32Window
        {
            public IntPtr Handle
            {
                get { return IntPtr.Zero; }
            }
        }

        public VssCredentialsUI(IWin32Window window)
        {
            _window = window ?? new ZeroWindowHandle();
        }

        Image _bitmap;
        public Image Image
        {
            get { return _bitmap; }
            set { _bitmap = value; }
        }

        public ISynchronizeInvoke Synchronizer
        {
            get { return _synchronizer; }
            set { _synchronizer = value; }
        }

		IUIService _uiService;
		public IUIService UIService
		{
			get { return _uiService; }
			set { _uiService = value; }
		}


		bool Reinvoke<T>(object sender, T e, EventHandler<T> handler)
			where T : EventArgs
		{
			if (Synchronizer != null && Synchronizer.InvokeRequired)
			{
				Synchronizer.Invoke(handler, new object[] { sender, e });
				return true;
			}

			return false;
		}

		DialogResult RunDialog(Form dialog)
		{
			if (dialog == null)
				throw new ArgumentNullException("dialog");

			if (UIService != null)
				return UIService.ShowDialog(dialog);
			else if (_window != null)
				return dialog.ShowDialog(_window);
			else
				return dialog.ShowDialog();
		}

        public void DialogUserNameHandler(Object sender, SvnUserNameEventArgs e)
        {
			if (Reinvoke(sender, e, DialogUserNameHandler))
				return;            

            using (UsernameDialog dlg = new UsernameDialog())
            {
                dlg.Text = "Connect to SourceSafe database";
                dlg.descriptionBox.Text = String.Format("The SourceSafe database at {0} requires a username", e.Realm);
                dlg.descriptionBox.Visible = true;
                dlg.rememberCheck.Enabled = e.MaySave;

                if (Image != null)
                    dlg.SetImage(Image);

                DialogResult r = RunDialog(dlg);

                if (r != DialogResult.OK)
                {
                    e.Cancel = e.Break = true;
                    return;
                }

                e.UserName = dlg.usernameBox.Text;
                e.Save = e.MaySave && dlg.rememberCheck.Checked;

            }
        }

        public void DialogUserNamePasswordHandler(Object sender, SvnUserNamePasswordEventArgs e)
        {
			if (Reinvoke(sender, e, DialogUserNamePasswordHandler))
				return;

            string description = String.Format("The SourceSafe database at {0} requires a username and password", e.Realm);
            if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= new Version(5, 1))
            {
                // If Windows XP/Windows 2003 or higher: Use the windows password dialog
                GetUserNamePasswordWindows(e, description);

                return;
            }

            using (UsernamePasswordDialog dlg = new UsernamePasswordDialog())
            {
                dlg.Text = "Connect to SourceSafe database";
                dlg.descriptionBox.Text = description;
                dlg.descriptionBox.Visible = true;
                dlg.rememberCheck.Enabled = e.MaySave;
                if (Image != null)
                    dlg.SetImage(Image);

                if (!string.IsNullOrEmpty(e.InitialUserName))
                {
                    dlg.usernameBox.Text = e.InitialUserName;
                    dlg.passwordBox.Select();
                }

				DialogResult r = RunDialog(dlg);

                if (r != DialogResult.OK)
                {
                    e.Save = dlg.rememberCheck.Checked;
                    e.UserName = e.Password = null;
                    e.Cancel = e.Break = true;
                    return;
                }

                e.UserName = dlg.usernameBox.Text;
                e.Password = dlg.passwordBox.Text;
                e.Save = e.MaySave && dlg.rememberCheck.Checked;
            }
        }

        private void GetUserNamePasswordWindows(SvnUserNamePasswordEventArgs e, string description)
        {
            NativeMethods.CREDUI_INFO info = new NativeMethods.CREDUI_INFO();
            info.pszCaptionText = "Connect to SourceSafe database";
            info.pszMessageText = description;
            info.hwndParent = (_window != null) ? _window.Handle : IntPtr.Zero;
            info.cbSize = Marshal.SizeOf(typeof(NativeMethods.CREDUI_INFO));

            StringBuilder sbUserName = new StringBuilder(e.InitialUserName ?? "", 1024);
            StringBuilder sbPassword = new StringBuilder("", 1024);

            bool dlgSave = false;

            int flags = (int)NativeMethods.CREDUI_FLAGS.GENERIC_CREDENTIALS | (int)NativeMethods.CREDUI_FLAGS.ALWAYS_SHOW_UI | (int)NativeMethods.CREDUI_FLAGS.DO_NOT_PERSIST;
            if (e.MaySave)
                flags |= (int)NativeMethods.CREDUI_FLAGS.SHOW_SAVE_CHECK_BOX;

            switch (NativeMethods.CredUIPromptForCredentials(ref info, e.Realm, IntPtr.Zero, 0, sbUserName, 1024, sbPassword, 1024, ref dlgSave,
                (NativeMethods.CREDUI_FLAGS)flags))
            {
                case NativeMethods.CredUIReturnCodes.NO_ERROR:
                    e.UserName = sbUserName.ToString();
                    e.Password = sbPassword.ToString();
                    e.Save = e.MaySave && dlgSave;
                    break;
                case NativeMethods.CredUIReturnCodes.ERROR_CANCELLED:
                    e.UserName = null;
                    e.Password = null;
                    e.Save = false;
                    e.Cancel = e.Break = true;
                    break;
            }
        }
	}
}
